package com.ian.config;

import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.web.client.RestTemplate;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * 覆盖默认的oauth2配置，以及覆盖了 application.yml 中的配置
 * 注：该类可以不存在，也能正常使用oauth2
 *
 * register a ClientRegistrationRepository @Bean
 * @author Witt
 * @version 1.0.0
 * @date 2022/5/5
 */
@Configuration
public class OAuth2LoginConfig {

    /**
     * 覆盖默认的oauth2代码配置，以及覆盖了 application.yml 中的配置
     * enable OAuth 2.0 login through httpSecurity.oauth2Login()
     * 注：该类可以不存在，也能正常使用oauth2
     *
     * @author Witt
     * @version 1.0.0
     * @date 2022/5/5
     */
    @EnableWebSecurity
    public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeHttpRequests(authorize -> authorize
                    // 配置放行 redirect_uri
//                    .antMatchers(HttpMethod.GET, "/authorization_code/**").permitAll()
                    .anyRequest().authenticated())
                    // .oauth2Login(withDefaults());
                    .oauth2Login(oauth2Login -> oauth2Login
                            .authorizationEndpoint(withDefaults())
                    .redirectionEndpoint(withDefaults())
                    .tokenEndpoint(withDefaults())
                    .userInfoEndpoint(withDefaults()));
        }
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        // 该构造器中可以放置多个 ClientRegistration
        return new InMemoryClientRegistrationRepository(giteeClientRegistration());
    }

    private ClientRegistration githubClientRegistration() {
        return ClientRegistration.withRegistrationId("github")
                .clientId("810cb8e57c48403ba804")
                .clientSecret("b4c405151bf0dea5c8be46548fec730dcb339172")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value
                // .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value
                // .scope("openid", "profile", "email", "address", "phone")
                .scope("read:user") // default value
                .authorizationUri("https://github.com/login/oauth/authorize") // default value
                .tokenUri("https://github.com/login/oauth/access_token") // default value
                .userInfoUri("https://api.github.com/user") // default value
                // .userNameAttributeName(IdTokenClaimNames.SUB)
                .userNameAttributeName("id") // default value
                // .jwkSetUri("")
                .clientName("Github") // default value
                .build();
    }

    private ClientRegistration giteeClientRegistration() {
        return ClientRegistration.withRegistrationId("gitee")
                .clientId("de91bdadc6b6b3599bebfa8755850fca0844aae44f13cc5e076110e961d4b59e")
                .clientSecret("6baa392358c93949b4f5e9e443dfa646746eb40c67ffa3eb87decb408d500b77")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) // default value
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) // default value
                // .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                /*
                默认回调地址
                注意：1 该地址必须和gitee中配置的应用回调地址相同，否则gitee授权页面报错：存在错误，无效的登录回调地址；
                     其他的 自定义回调地址 的注意事项，这里统统不需要。
                 */
                .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // default value
                /*
                 自定义回调地址
                 注意：1 需要自定义一个Controller方法来处理该回调请求；
                      2 该地址必须和gitee中配置的应用回调地址相同，否则gitee授权页面报错：存在错误，无效的登录回调地址；
                      3 需要 在 SecurityConfig 配置类中配置放行该uri（如此处配置的 /authorization_code），
                            否则一到重定向url，由于没有权限，立即跳到login页面，然后又跳到gitee的授权页面，最终网页提示：你重定向次数过多。
                      4 在请求access_token时（发起post请求 https://gitee.com/oauth/token），必须携带该参数“redirect_uri”，
                            否则报错：org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]

                 最终发现，自定义回调地址，还是有问题，本地还是无法认证成功，只是能从gitee获取一些用户信息而已。
                    还是默认的回调地址吧。
                 */
//                .redirectUri("http://localhost:8050/authorization_code")
                // .scope("openid", "profile", "email", "address", "phone")
//                .scope("user") // default value，但是报错：存在错误，请求范围无效、未知或格式不正确
                .authorizationUri("https://gitee.com/oauth/authorize") // default value
                .tokenUri("https://gitee.com/oauth/token") // default value
                .userInfoUri("https://gitee.com/api/v5/user") // default value
                // .userNameAttributeName(IdTokenClaimNames.SUB)
                .userNameAttributeName("id") // default value
                // .jwkSetUri("")
                .clientName("Gitee") // default value
                .build();
    }
}
